home *** CD-ROM | disk | FTP | other *** search
- /* @(#)util/gleem.c 1.4 7/11/92 11:39:57 */
- /*
- * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
- * Copyright (C) 1992 Ronald S. Karr
- *
- * See the file COPYING, distributed with smail, for restriction
- * and warranty information.
- */
-
- /*
- * gleem - gleem useful map info from standard input
- *
- * usage:
- * gleem [-n name] [-c] [-f [-d dir]] [-p] [-s] [-F] file ...
- *
- * -n name name mode, convert to: name { ... } form,
- * imples -c, ignores -s
- * -c strip most comments
- * -f print: file { filename } before reading each file
- * -d dir add dir to filenames not starting with '/'
- * -p print: private {} after reading each file
- * -s convert delete & adjust directives to whitespace + error
- * -F convert file directives to spaces + error
- * file ... files to read, - means stdin & -f shows [stdin]
- *
- *
- * If name mode: (-n name)
- *
- * Since -c is implied, comments and blank lines are discarded.
- * If the entire line is discarded, no further processing is done.
- *
- * Next, the <line> is processed as follows: The whitespace
- * within ()'s is removed. Whitespace between a token and a
- * '(' is removed. All tokens are separated by commas and all
- * whitespace is removed. The final comma and newline, if
- * any, is removed.
- *
- * Last, the string: "name {<line>}" is printed.
- *
- * In not name mode: (no -n)
- *
- * Print the contents of each file, striping comments if -c was given.
- *
- * A comment begins with a # and ends with a newline. A token is a set of
- * chars that does not include whitespace, commas and ()'s. A whitespace
- * char is a tab newline. Blank lines are lines containing zero or more
- * whitespace chars.
- *
- * Note: Comment lines are not completely stripped. Doing so would alter the
- * line counts and throw off error reporting. Also pathalias allows
- * comments to exist in the middle of a line. If a comment is found
- * in the middle of the line, it is replaced by a single '#'. If
- * the single '#' would start the line, a space is placed in before
- * the single '#'.
- */
-
- #include <stdio.h>
- #include "defs.h"
-
- #define BUFFER_SIZE 4096 /* length of longest line */
-
- void pack(); /* pack a line in second stage */
- void strip_com(); /* strip comments from a string */
- void cat_file(); /* cat a file in a special way */
- void usage(); /* print a usage message and exit */
- void safemap(); /* perform safemap striping */
- void file_2_space(); /* convert file {...} to spaces */
- char *program; /* our name */
- int errors = 0; /* 1 ==> errors were encountered */
-
- main(argc, argv)
- int argc; /* arg count */
- char *argv[]; /* args */
- {
- int c; /* operand */
- int file_to_space = 0; /* 1 ==> convert file to spaces */
- int strip_comments = 0; /* 1 ==> strip input comments */
- int safemap_mode = 0; /* 1 ==> remove delete, adjust lines */
- int print_private = 0; /* 1 ==> print private {} */
- int print_filename = 0; /* 1 ==> print file { filename } */
- char *dirname = NULL; /* dirname for non / based files */
- char *name = NULL; /* non-NULL ==> name mode */
- char *filename; /* the filename we are reading */
- extern int optind; /* operand index */
- extern char *optarg; /* operand argument */
-
- /*
- * parse args
- */
- program = argv[0];
- while ((c = getopt(argc, argv, "n:cfd:psF")) != EOF) {
- switch (c) {
- case 'n': /* token name, token mode */
- name = optarg;
- break;
- case 'c': /* strip comments */
- strip_comments = 1;
- break;
- case 'f': /* print leading file { filename } names */
- print_filename = 1;
- break;
- case 'd': /* directory base for non / based files */
- dirname = optarg;
- break;
- case 'p': /* print trailing private {} */
- print_private = 1;
- break;
- case 's': /* strip delete and adjust directives */
- safemap_mode = 1;
- break;
- case 'F': /* convert file directives to spaces */
- file_to_space = 1;
- break;
- default:
- usage();
- break;
- }
- }
- /* be sure we have filenames */
- if (optind >= argc) {
- usage();
- }
- /* name mode -n imples -c, disables -s */
- if ( name != NULL ) {
- strip_comments = 1; /* -c implied */
- safemap_mode = 0; /* -s disabled */
- }
-
- /*
- * process each file
- */
- for (filename=argv[optind]; optind < argc; ++optind,filename=argv[optind]) {
-
- /*
- * if -f, print a file directive
- */
- if ( print_filename ) {
-
- /* catch the stdin case */
- if (strcmp(filename, "-") == 0) {
- puts("file {[stdin]}");
-
- /* catch the /-root based filename, or if we have no dirname */
- } else if (filename[0] == '/' || dirname == NULL) {
- printf("file {%s}\n", filename);
-
- /* non /-root based file, append dirname */
- } else {
- printf("file {%s/%s}\n", dirname, filename);
- }
- }
-
- /*
- * proces the file
- */
- cat_file(filename, name, strip_comments, safemap_mode, file_to_space);
-
- /*
- * print a private directive if needed
- */
- if ( print_private ) {
- puts("private {}");
- }
- }
-
- /*
- * exit noting if errors happened
- */
- exit(errors);
- }
-
- /*
- * pack - pack a line for name mode
- *
- * The whitespace within ()'s is removed. Whitespace between a token
- * and a '(' is removed. All tokens are separated by commas and
- * all whitespace is removed. The final comma and newline, if
- * any, is removed.
- */
- void
- pack( line )
- char *line; /* the line to pack */
- {
- int in_paren; /* 1 ==> inside ()'s */
- int space; /* 1 ==> last char was a space */
- int comma; /* 1 ==> last char was a comma */
- char *p; /* pointer */
- char *q; /* pointer */
- char buf[BUFFER_SIZE+3]; /* I/O buffer */
- char *malloc(); /* stroage allocator */
-
- /*
- * remove whitespace within ()'s
- */
- q = line; /* from */
- p = buf; /* to */
- in_paren = 0; /* not inside ()'s */
- while ( *q ) {
- /* detect open and close ()'s */
- switch (*q) {
- case '(':
- in_paren = 1;
- *p++ = *q++;
- break;
- case ')':
- in_paren = 0;
- *p++ = *q++;
- break;
- case ' ':
- case '\t':
- if (in_paren == 0) {
- *p++ = *q++;
- } else {
- ++q; /* skip whitespace insize a () */
- }
- break;
- case '\n':
- ++q; /* skip newline */
- break;
- default:
- *p++ = *q++;
- break;
- }
- }
- *p = '\0';
-
- /*
- * remove leading whitespace, convert multiple spaces into a
- * comma, except when before a ( or a comma
- */
- q = buf; /* from */
- p = line; /* to */
- space = 0; /* have not seen spaces */
- comma = 0; /* have not seen commas */
- while (*q == ' ' || *q == '\t') { /* skip leading whitespace */
- ++q;
- }
- while ( *q ) {
- switch (*q) {
- case ' ':
- case '\t':
- ++q; /* skip whitespace until later, maybe */
- space = 1;
- break;
- case '(':
- *p++ = *q++; /* no comma before ( or comma */
- space = 0;
- comma = 0;
- break;
- case ',':
- if (comma == 0) {
- *p++ = *q++; /* copy only one comma */
- comma = 1;
- } else {
- ++q; /* skip multiple commas */
- }
- break;
- default:
- if (space == 1 && comma == 0) {
- *p++ = ','; /* replace multi whitespace with a comma */
- }
- comma = 0;
- *p++ = *q++;
- space = 0;
- break;
- }
- }
-
- /*
- * remove any trailing commas
- */
- while (--p > line && *p == ',') {
- }
- if (*p == ',') {
- *p = '\0';
- } else {
- *(p+1) = '\0';
- }
- }
-
- /*
- * strip_com - perform -c comment striping
- *
- * This routine removes most comments and all trailing whitespace from
- * the string passed to it. The trailing newline is always removed.
- *
- * A comment begins with a '#' and ends with a newline. A whitespace
- * char is a tab newline. Blank lines are lines containing zero or
- * more whitespace chars.
- *
- * Note: Comment lines are not completely stripped. Doing so would alter the
- * line counts and throw off error reporting. Also pathalias allows
- * comments to exist in the middle of a line. If a comment is found
- * in the middle of the line, it is replaced by a single '#'. If
- * the single '#' would start the line, a space is placed in before
- * the single '#'.
- */
- void
- strip_com( buf )
- char *buf; /* string to strip */
- {
- register char *p; /* index */
- int found_comment=0; /* 1 => we removed a #comment */
-
- /*
- * remove a comment
- */
- p = index(buf, '#');
- if (p != NULL) {
-
- /* strip */
- *p = '\0';
-
- /* if entire lne was a comment, remove it all and return */
- if (p == buf) {
- return;
- }
-
- /* note mid line comment found */
- found_comment = 1;
- }
-
- /*
- * scan over any trailing whitespace
- */
- p = buf + strlen(buf) - 1;
- while (p >= buf && (*p == ' ' || *p == '\t' || *p == '\n')) {
- --p;
- }
-
- /*
- * clear out any whitespace
- *
- * If we removed a comment, put the '#' back.
- */
- if (found_comment) {
- /* if the '#' would start a line, blank fill first */
- if (p < buf) {
- *++p = ' ';
- }
- /* minimal comment */
- *++p = '#';
- }
- *++p = '\0'; /* strip */
- return;
- }
-
- /*
- * cat_file - output a file
- *
- * opens a filename, writes it contents to stadard output. Strips comments,
- * removes problem lines and pack for name mode if needed. Problems lines
- * are lines that begin with the token "delete" or "adjust" or "file".
- */
- void
- cat_file( filename, name, strip_comments, safemap_mode, file_to_space )
- char *filename; /* the name of the file to output */
- char *name; /* non-NULL ==> name mode */
- int strip_comments; /* 1 ==> strip input comments */
- int safemap_mode; /* 1 ==> remove delete, adjust lines */
- int file_to_space; /* 1 ==> convert file to spaces */
- {
- int linenum; /* current line number */
- char buf[BUFFER_SIZE+3]; /* I/O buffer */
- FILE *stream; /* file stream to read */
-
- /*
- * try to open the file
- */
- if (strcmp(filename, "-") == 0) {
- stream = stdin; /* - ==> read from stdin */
- filename = "[stdin]";
- } else {
- stream = fopen(filename, "r");
- }
- if (stream == NULL) {
- /* unable to open, object and return */
- fprintf(stderr, "%s: unable to open file: %s\n", program, filename);
- errors = 1;
- return;
- }
-
- /*
- * process each line
- *
- * We allow lines to be up to BUFFER_SIZE chars long. But we
- * read BUFFER_SIZE+1 chars (i.e., fgets value of BUFFER_SZIE+2)
- * chars to detect a longer line. If the line were BUFFER_SIZE+1
- * chars, then the BUFFER_SIZE+1-th char would not be '\0'.
- */
- clearerr(stream); /* no errors yet */
- linenum = 0; /* no lines read yet */
- buf[BUFFER_SIZE] = '\0'; /* used to detect line long lines */
- while (fgets(buf, BUFFER_SIZE+2, stream) != NULL) {
-
- /*
- * count the line
- */
- ++linenum;
-
- /*
- * firewall - see if the line is too long
- */
- if (buf[BUFFER_SIZE] != '\0') {
- fprintf(stderr, "%s: file: %s line: %d longer than %d chars\n",
- program, filename, linenum, BUFFER_SIZE);
- errors = 1;
- break;
- }
-
- /*
- * strip comments if needed
- */
- if (name != NULL || strip_comments) {
- strip_com(buf);
- }
-
- /*
- * convert file directives to spaces if needed
- */
- if (file_to_space) {
- file_2_space(buf);
- }
-
- /*
- * pack the line, if name mode
- */
- if (name != NULL) {
- pack(buf);
- }
-
- /*
- * perform safemap processing if needed
- */
- if (safemap_mode && name == NULL) {
- safemap(filename, linenum, buf);
- }
-
- /*
- * print the newly formed line
- */
- if (name != NULL) {
- /* output in name mode style */
- if (buf[0] != '\0') {
- printf("%s {%s}\n", name, buf);
- }
- } else {
- /* output line, being sure to write a newline */
- if ( strip_comments ) {
- puts(buf); /* replace the stripped newline */
- } else {
- fputs(buf, stdout); /* newline is already there */
- }
- }
- }
-
- /*
- * examine stop reason
- */
- if ( ferror(stream) ) {
- fprintf(stderr, "%s: error in reading file: %s line: %d\n",
- program, filename, linenum);
- errors = 1;
- }
-
- /*
- * flush & close if not stdin
- */
- fflush(stdout);
- if (stream != stdin) {
- (void) fclose(stream);
- }
- }
-
- /*
- * usage - print usage message and exit
- */
- void
- usage()
- {
- fprintf(stderr,
- "usage: %s [-n name] [-f [-d dir]] [-c] [-p] [-s] file ...\n",
- program);
- exit(2);
- }
-
- /*
- * safemap - avoid all delete, adjust and file directives
- *
- * Any lines of the form:
- *
- * ^delete[ \t]*{
- * ^adjust[ \t]*{
- *
- * are removed. The 'delete' and 'adjust' result in errors.
- */
- void
- safemap( filename, linenum, buf )
- char *filename; /* filename or [stdin] being read */
- int linenum; /* current line number */
- char *buf; /* the buffer to check for unsafe words */
- {
- register char *p; /* pointer */
-
- /*
- * rule out good lines quickly
- */
- switch (buf[0]) {
- case 'd':
- case 'a':
- break;
- default:
- return; /* nothing unsafe about this line */
- }
-
- /*
- * begins with an unsafe letter, is it really an unsafe word?
- */
- if (strncmp(buf, "delete", 6) == 0) {
-
- /* leading chars are ok, but is if followed by space/tab? */
- for (p = buf+6; *p; ++p) {
-
- /* look for start of {} group after word */
- if (*p == '{' /*}*/) {
- /* we found end of word, start of {} group, unsafe! */
- buf[0] = '\0';
- fprintf(stderr,
- "%s: bogus delete directive in file: %s line: %d\n",
- program, filename, linenum);
- errors = 1;
- break;
- }
-
- /* must be between words then */
- if (*p != ' ' && *p != '\t') {
- break; /* not an unsafe word */
- }
- }
-
- } else if (strncmp(buf, "adjust", 6) == 0) {
-
- /* leading chars are ok, but is if followed by space/tab? */
- for (p = buf+6; *p; ++p) {
-
- /* look for start of {} group after word */
- if (*p == '{' /*}*/) {
- /* we found end of word, start of {} group, unsafe! */
- buf[0] = '\0';
- fprintf(stderr,
- "%s: bogus adjust directive in file: %s line: %d\n",
- program, filename, linenum);
- errors = 1;
- break;
- }
-
- /* must be between words then */
- if (*p != ' ' && *p != '\t') {
- break; /* not an unsafe word */
- }
- }
- }
- return;
- }
-
- /*
- * file_2_space - convert file {...} lines to spaces
- *
- * This allows one to strip file directives from uuwho input without changing
- * the position within the file.
- */
- void
- file_2_space( buf )
- char *buf; /* the string to examine */
- {
- char *p; /* pointer */
-
- /*
- * look for a file directive
- */
- if (strncmp(buf, "file", 4) == 0) {
-
- /* leading chars are ok, but is if followed by space/tab? */
- for (p = buf+4; *p; ++p) {
-
- /* look for start of {} group after word */
- if (*p == '{' /*}*/) {
-
- /*
- * convert a file directive into spaces
- */
- for (p=buf; *p && *p != '\n'; ++p) {
- *p = ' ';
- }
- return;
- }
-
- /* must be between words then */
- if (*p != ' ' && *p != '\t') {
- break; /* not an unsafe word */
- }
- }
- }
- return;
- }
-